//sample.cpp
///////////////////////////////////////////////////////////////////////////////
//
//	Pug XML Parser Sample.
//
///////////////////////////////////////////////////////////////////////////////


#include "stdafx.h"


//Define this in any implementation, before "pugxml.h", to be notified of API campatibility.
#define PUGAPI_VARIANT 0x58475550	//The Pug XML library variant we are using in this implementation.
#define PUGAPI_VERSION_MAJOR 1		//The Pug XML library major version we are using in this implementation.
#define PUGAPI_VERSION_MINOR 2		//The Pug XML library minor version we are using in this implementation.
//Include the Pug XML library.
#include "pugxml.h"


using namespace std;
using namespace pug;


//Example traversal filter for xml_node::traverse(). See Example A.
class xml_outline : public xml_tree_walker
{
public:
	const TCHAR* map_to_type_name(xml_node_type type) //Convert some entity types to text.
	{
		switch(type)
		{
		case node_pcdata: return _T("PCDATA");
		case node_cdata: return _T("CDATA");
		case node_comment: return _T("COMMENT");
		case node_pi: return _T("PI");
		case node_document: return _T("DOCUMENT");
		default: return _T("UNKNOWN");
		}
	}
public:
	//Traversal begin callback.
	virtual bool begin(xml_node& node)
	{
		cout << _T("BEGIN(") << map_to_type_name(node.type()) << _T(")") << endl;
		return true;
	}
	//Traversal node callback; cumulatively outputs a simple document outline.
	virtual bool for_each(xml_node& node)
	{
		for(long i=0; i<depth(); ++i) cout << _T("| ");
		if(node.has_child_nodes()) cout << _T("+ ") << node.name() << endl;
		else if(node.type_element()||node.type_dtd_item()) cout << _T("- ") << node.name() << endl;
		else cout << _T("- (") << map_to_type_name(node.type()) << _T(")") << endl;
		return true; //Keep traversing.
	}
	//Traversal end callback.
	virtual bool end(xml_node& node)
	{
		cout << _T("END(") << map_to_type_name(node.type()) << _T(")") << endl;
		return true;
	}
};


int _tmain(int argc, _TCHAR* argv[])
{

#if defined(WIN32) && defined(_DEBUG)
	//Suggested by Neville Franks to avoid prematurely dumping memory leaks.
	const int _CrtDbgFlags=_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
	_CrtSetDbgFlag(_CrtDbgFlags|_CRTDBG_LEAK_CHECK_DF);
#endif

	if(argc < 2)
	{
		cout << _T("Usage: ") << argv[0] << _T(" [file]") << endl;
		return 0;
	}


	//Verify the library at runtime.
	if(pug::lib_variant() != PUGAPI_VARIANT)
	{	cout << _T("Incompatible library variant!") << endl; return -1; }
	unsigned long version = pug::lib_version();
	if(HIWORD(version) != PUGAPI_VERSION_MAJOR)
	{	cout << _T("Incompatible library major version!") << endl; return -1; }
	if(LOWORD(version) != PUGAPI_VERSION_MINOR)
	{	cout << _T("Incompatible library minor version!") << endl; return -1; }


	xml_parser* xml = new xml_parser(); //Construct.


	//////////////////////////////////////////
	// Example A - Parse a file from disk.
	//////////////////////////////////////////
	cout << _T("Example A - Parse a file & traverse it.") << endl
	     << _T("--------------------------------------") << endl;
	if
	(
#ifdef PUGOPT_MEMFIL
		xml->parse_mmfile	//Parse the file, with options, in memory-mapped mode.
#else  //!PUGOPT_MEMFIL
		xml->parse_file		//Parse the file, with options.
#endif //PUGOPT_MEMFIL
		(
			argv[1],
			(
				//parse_dtd_only		| //Parse only '<!DOCTYPE [*]>'
				parse_doctype		| //Parse '<!DOCTYPE ...>' section, with '[...]' as data member.
#ifndef PUGOPT_MEMFIL				  //I get all kinds of problems parsing the DTD in memory-mapped mode. KW.
				parse_dtd			| //Parse whatever is in DOCTYPE data member ('[...]').
#endif  //PUGOPT_MEMFIL
				parse_pi			| //Parse '<?...?>'
				parse_cdata			| //Parse '<![CDATA[...]]>', and '<![INCLUDE[...]]>'
				parse_comments		| //Parse <!--...-->'
#ifndef PUGOPT_MEMFIL				
				parse_wnorm			| //Normalize all entities that are flagged to be trimmed.
#endif  //PUGOPT_MEMFIL
				parse_trim_entity	| //Trim DTD entities.
				parse_trim_attribute| //Trim 'foo="..."'.
				parse_trim_pcdata	| //Trim '>...<'
				parse_trim_cdata	| //Trim '<![CDATA[...]]>'
				parse_trim_comment	  //Trim <!--...-->'
			)
		)
	)
	{
		cout << _T("1.) Outline it using a tree walker.") << endl;
		xml_outline outline;
		xml_node document = xml->document();
		document.traverse(outline); //Output the resulting tree using our outline filter.
		//Use the iterators.
		string itname;
		cout << _T("2.) Use iterators to examine an element: ");
		cin >> itname;
		cout << _T("Finding element...") << endl;
		xml_node itelem = document.first_element_by_name(itname); //Find some element.
		if(!itelem.empty())
		{
			cout << _T("OK: Found it.") << endl;
			xml_node::child_iterator i = itelem.children_begin(); //For each child.
			xml_node::child_iterator m = itelem.children_end();
			for(; i < m; ++i)
			{
				cout << i->name() << _T(" [ ");
				xml_node::attribute_iterator j = i->attributes_begin(); //For each child attribute.
				xml_node::attribute_iterator n = i->attributes_end();
				for(; j != n; ++j)
					cout << j->name() << _T("=") << j->value() << _T(" ");
				cout << _T("]") << endl;
			}
		} else cout << _T("No such element '") << itname << _T("'") << endl;
	}
	xml->clear(); //clear for next test.
	cout << _T("--------------------------------------") << endl;
	

	//////////////////////////////////////////
	// Example B - Build a tree from scratch.
	//////////////////////////////////////////
	cout << _T("Example B - Build a tree from scratch.") << endl
		 << _T("--------------------------------------") << endl;
	xml->create();
	xml_node root = xml->document();
	if(!root.empty())
	{
		xml_node pi = root.append_child(node_pi); //Add a PI section.
		pi.name(_T("xml")); //Give it a name.
		xml_attribute version = pi.append_attribute(_T("version"),123.456789); //Add an attribute.
		//Fool around with the overloaded operators.
		version << 7777777.0;
		double v = 0.0;
		version >> v;
		string s = _T("1.0");
		s = (string)version;
		version >> s;
		//Set some attributes & data.
		pi.attribute(_T("encoding")) = _T("UTF-8");
		xml_node example = root.append_child(node_element); //Add a child element.
		example.name(_T("example")); //Give it a name.
		example.attribute(_T("is-simple")) = false; //Add some attributes.
		example.attribute(_T("example-id")) = _T("B");
		example.attribute(_T("i-am-a-goose")) = true;
		example.attribute(_T("type-of-goose")) = _T("Branta Canadensis Maxima");
		example.attribute(_T("goosely-wingspan")) = 6.54321;
		example.attribute(_T("goosely-wingspan-units")) = _T("US-Feet");
		xml_node comment = example.append_child(node_comment); //Add a comment.
		comment.value(_T("May I make a suggestion?"));
		xml_node data = example.append_child(node_pcdata); //Add some PCDATA.
		data.value(_T("Honk whenever you please."));
		xml_node thingy = example.append_child(node_element); //Add a sub-element.
		thingy.name(_T("a-goosely-sound"));
		thingy.append_child(node_pcdata).value(_T("Honketty-harrooo!"));
	}
	cout << root << endl << endl;
	xml->clear(); //Clear for the next test.
	cout << _T("--------------------------------------") << endl;


	///////////////////////////////////////
	// Example C - Query a tree.
	///////////////////////////////////////
	cout << _T("Example C - Query a tree.") << endl
	     << _T("--------------------------------------") << endl;
	cout << _T("XML file to load: ");
	TCHAR path[MAX_PATH] = {0};
	cin >> path;

#ifdef PUGOPT_MEMFIL
		if(xml->parse_mmfile(path)) //Parse the file, in memory-mapped mode.
#else  //!PUGOPT_MEMFIL
		if(xml->parse_file(path)) //Parse the file.
#endif //PUGOPT_MEMFIL
	{
		TCHAR name[64] = {0};
		TCHAR attr[64] = {0};
		TCHAR value[512] = {0};
		//1.) Find an element with attribute.
		cout << _T("1.) Find an element with attribute (<element * attribute=\"value\" *>).") << endl;
		cout << _T("Element name to query: ");
		cin >> name;
		cout << _T("Attribute to query: ");
		cin >> attr;
		cout << _T("Attribute value to query: ");
		cin >> value;
		xml_node entry = xml->document().first_element_by_attribute(name,attr,value); //Search for any tag having '<name attr="value">'.
		if(!entry.empty()) //Ensure that it was found.
		{
			cout << _T("Result: Query matched:") << endl << endl;
			string path = entry.path(); //What is the branch's path?
			if(!path.empty()) cout << _T("Branch path: ") << path << endl << endl;
			else cout << _T("Branch path: ???") << endl << endl;
			cout << entry << endl;
		}
		else cout << _T("Result: No such element.") << endl;

		//2.) Find element by path.
		cout << _T("2.) Find element by path (relative to last search).") << endl;
		cout << _T("    E.g. './foo/bar' (relative) -or- '../foo/bar' (relative) -or- '/foo/bar' (absolute).") << endl;
		cout << _T("Element path to query: ");
		cin >> value;
		entry = entry.first_element_by_path(value);
		if(!entry.empty()) //Ensure that it was found.
		{
			cout << _T("Result: Query matched:") << endl << endl;
			string path = entry.path(); //What is the branch's path?
			if(!path.empty()) cout << _T("Branch path: ") << path << endl << endl;
			else cout << _T("Branch path: ???") << endl << endl;
			cout << entry << endl;
		}
		else cout << _T("Result: No such path.") << endl;
		
		//3.) Find all elements of a given name.
		cout << _T("3.) Find all elements of a given name (<element *>)+.") << endl;
		cout << _T("Element name to query: ");
		cin >> name;
		xml_node_list found;
		xml->document().all_elements_by_name(name,found);
		if(found.size())
		{
			cout << _T("Result: Query matched:") << endl;
			cout << found;
		} 
		else cout << _T("Result: No such element.") << endl;
	}
	else cout << _T("No such file.") << endl;
	cout << _T("--------------------------------------") << endl;


	delete xml; //Free


	return 0;
}


